package org.gdstash.ui;

import org.gdstash.item.GDItem;
import org.gdstash.ui.table.GDItemNameSorter;
import org.gdstash.ui.table.GDItemNameTableModel;
import org.gdstash.ui.util.AdjustablePanel;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class GDItemNameTablePane extends AdjustablePanel {
    private List<GDItem> data;
    private GDItemNameTableModel model;
    private JTable table;
    private GDItem selItem;
    private List<ActionListener> listeners;
    private boolean inclFaction;

    public GDItemNameTablePane(final List<GDItem> list, final boolean inclFaction) {
        this.inclFaction = inclFaction;
        this.listeners = new LinkedList<ActionListener>();
        this.selItem = null;
        this.data = list;
        this.init();
    }

    @Override
    public void adjustUI() {
        Font fntLabel = UIManager.getDefaults().getFont("Label.font");
        Font fntTable = UIManager.getDefaults().getFont("TableHeader.font");
        if (fntTable == null) {
            fntTable = fntLabel;
        }
        fntLabel = fntLabel.deriveFont((float) GDStashFrame.iniConfig.sectUI.fontSize);
        fntTable = fntTable.deriveFont((float) GDStashFrame.iniConfig.sectUI.fontSize);
        if (this.table == null) {
            this.table = new JTable();
            this.table.getSelectionModel().addListSelectionListener(new GDListListener());
            this.table.setSelectionMode(0);
            this.table.addMouseMotionListener(new HoverListener());
            final GDItemNameSorter sorter = new GDItemNameSorter();
            this.table.setRowSorter((RowSorter<? extends TableModel>) sorter);
            this.table.getTableHeader().addMouseListener(new SortListener());
            this.setItems(null);
        }
        this.table.getTableHeader().setFont(fntTable);
        this.table.setFont(fntLabel);
        int height = -1;
        if (this.data != null) {
            for (final GDItem item : this.data) {
                final BufferedImage img = item.getImage();
                if (img != null && img.getHeight() > height) {
                    height = img.getHeight();
                }
            }
        }
        if (height == -1) {
            height = 12;
        }
        if (height != -1) {
            height = height * GDStashFrame.iniConfig.sectUI.graphicScale / 100;
            this.table.setRowHeight(height);
        }
        final GDItemNameTableModel model = (GDItemNameTableModel) this.table.getModel();
        model.adjustUI();
        this.updateTableColumns();
    }

    public void refresh() {
        if (this.table != null) {
            this.setItems(null);
        }
    }

    private void init() {
        this.adjustUI();
        this.setItems(this.data);
        final JScrollPane scroll = new JScrollPane(this.table);
        scroll.getVerticalScrollBar().setUnitIncrement(2 * GDStashFrame.iniConfig.sectUI.fontSize);
        GroupLayout layout = null;
        GroupLayout.SequentialGroup hGroup = null;
        GroupLayout.SequentialGroup vGroup = null;
        layout = new GroupLayout(this);
        this.setLayout(layout);
        layout.setAutoCreateGaps(true);
        layout.setAutoCreateContainerGaps(true);
        hGroup = layout.createSequentialGroup();
        hGroup.addGroup(layout.createParallelGroup().addComponent(scroll));
        layout.setHorizontalGroup(hGroup);
        vGroup = layout.createSequentialGroup();
        vGroup.addGroup(layout.createParallelGroup().addComponent(scroll));
        layout.setVerticalGroup(vGroup);
    }

    private void updateTableColumns() {
        this.table.getColumnModel().getColumn(1).setCellRenderer(new GDItemTableCellRenderer());
        this.table.getColumnModel().getColumn(2).setCellRenderer(new GDItemTableCellRenderer());
        int scale = 100;
        int size = 12;
        if (GDStashFrame.iniConfig != null) {
            scale = GDStashFrame.iniConfig.sectUI.graphicScale;
            size = GDStashFrame.iniConfig.sectUI.fontSize;
        }
        this.table.getColumnModel().getColumn(0).setPreferredWidth(120 * scale / 100);
        this.table.getColumnModel().getColumn(1).setPreferredWidth(42 * size);
        this.table.getColumnModel().getColumn(2).setPreferredWidth(6 * size);
    }

    public List<GDItem> getItemList() {
        return this.data;
    }

    public void setItems(final List<GDItem> list) {
        this.data = list;
        if (this.data != null) {
            Collections.<GDItem>sort(this.data);
        }
        int height = -1;
        if (this.data != null) {
            final Iterator<GDItem> iter = this.data.iterator();
            while (iter.hasNext()) {
                final GDItem item = iter.next();
                final BufferedImage img = item.getImage();
                if (img == null) {
                    iter.remove();
                } else {
                    if (img.getHeight() <= height) {
                        continue;
                    }
                    height = img.getHeight();
                }
            }
        }
        if (height == -1) {
            height = 12;
        }
        this.model = new GDItemNameTableModel(this.data, this.inclFaction);
        this.table.setModel(this.model);
        this.table.setSelectionMode(0);
        this.table.getSelectionModel().addListSelectionListener(this.table);
        final GDItemNameSorter sorter = new GDItemNameSorter(this.model);
        this.table.setRowSorter((RowSorter<? extends TableModel>) sorter);
        final Rectangle rect = this.table.getVisibleRect();
        rect.setLocation(0, 0);
        this.table.scrollRectToVisible(rect);
        if (height != -1) {
            height = height * GDStashFrame.iniConfig.sectUI.graphicScale / 100;
            this.table.setRowHeight(height);
        }
        this.updateTableColumns();
    }

    public void updateItem(final GDItem item) {
        if (item == null) {
            this.deleteItem();
            return;
        }
        int pos = this.table.getSelectedRow();
        if (pos == -1) {
            return;
        }
        final GDItemNameTableModel model = (GDItemNameTableModel) this.table.getModel();
        model.updateRow(pos, item);
        final ListSelectionModel lsm = this.table.getSelectionModel();
        final int rows = model.getRowCount();
        if (rows > 0) {
            if (rows <= pos) {
                pos = rows - 1;
            }
            lsm.setSelectionInterval(pos, pos);
            final GDItem gdi = model.getItem(pos);
            this.setSelectedItem(gdi);
        } else {
            this.table.clearSelection();
            this.selItem = null;
        }
    }

    public void deleteItem() {
        int pos = this.table.getSelectedRow();
        if (pos == -1) {
            return;
        }
        final GDItemNameTableModel model = (GDItemNameTableModel) this.table.getModel();
        model.removeRow(pos);
        final ListSelectionModel lsm = this.table.getSelectionModel();
        final int rows = model.getRowCount();
        if (rows > 0) {
            if (rows <= pos) {
                pos = rows - 1;
            }
            lsm.setSelectionInterval(pos, pos);
            final GDItem item = model.getItem(pos);
            this.setSelectedItem(item);
        } else {
            this.table.clearSelection();
            this.selItem = null;
        }
    }

    public void clearSelection() {
        this.table.clearSelection();
    }

    public GDItem getSelectedItem() {
        return this.selItem;
    }

    private void setSelectedItem(final GDItem item) {
        this.selItem = item;
        if (this.selItem != null && item.isStackable()) {
            (this.selItem = this.selItem.clone()).setStackCount(this.selItem.getDefaultStackSize());
        }
        this.fireActionEvent();
    }

    public void addActionListener(final ActionListener listener) {
        this.listeners.add(listener);
    }

    public void removeActionListener(final ActionListener listener) {
        final Iterator<ActionListener> iter = this.listeners.iterator();
        while (iter.hasNext()) {
            final ActionListener l = iter.next();
            if (listener == l) {
                iter.remove();
            }
        }
    }

    private void fireActionEvent() {
        final ActionEvent ev = new ActionEvent(this, 1001, null);
        for (final ActionListener l : this.listeners) {
            l.actionPerformed(ev);
        }
    }

    private class GDItemTableCellRenderer extends DefaultTableCellRenderer {
        @Override
        public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, final boolean hasFocus, final int row, final int col) {
            if (col < 1) {
                return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
            }
            final JLabel label = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
            final GDItemNameTableModel model = (GDItemNameTableModel) table.getModel();
            label.setForeground(model.getCellTextColor(row, col));
            return label;
        }
    }

    private class GDListListener implements ListSelectionListener {
        @Override
        public void valueChanged(final ListSelectionEvent e) {
            final ListSelectionModel model = (ListSelectionModel) e.getSource();
            if (model.getValueIsAdjusting()) {
                return;
            }
            if (model.isSelectionEmpty()) {
                GDItemNameTablePane.this.setSelectedItem(null);
                return;
            }
            final int min = e.getFirstIndex();
            final int max = e.getLastIndex();
            int sel = -1;
            for (int i = min; i <= max; ++i) {
                if (model.isSelectedIndex(i)) {
                    sel = i;
                    break;
                }
            }
            GDItemNameTablePane.this.setSelectedItem(GDItemNameTablePane.this.model.getItem(sel));
        }
    }

    private class SortListener extends MouseAdapter {
        @Override
        public void mouseClicked(final MouseEvent ev) {
            final int idx = GDItemNameTablePane.this.table.getColumnModel().getColumnIndexAtX(ev.getX());
            final GDItemNameSorter sorter = (GDItemNameSorter) GDItemNameTablePane.this.table.getRowSorter();
            sorter.sort();
        }
    }

    private class HoverListener extends MouseMotionAdapter {
        private int currRow;

        private HoverListener() {
            this.currRow = -1;
        }

        @Override
        public void mouseMoved(final MouseEvent e) {
            final JTable table = (JTable) e.getSource();
            final int col = table.columnAtPoint(e.getPoint());
            if (col != 0) {
                return;
            }
            final int row = table.rowAtPoint(e.getPoint());
            if (row > -1 && row < table.getRowCount() && this.currRow != row) {
                this.currRow = row;
                table.getSelectionModel().setSelectionInterval(row, row);
            }
        }
    }
}
